package util;

import gui.StoiData;
import util.exceptions.UnknownElementException;

import java.util.ArrayList;

public class Stoichiometry {


    private Equation eq;
    private ArrayList<Molecule> limiting;
    private double moles;
    private int count;


    private static double getMolarMass(Molecule molecule) {
        Molecule g = molecule;
        ArrayList<MoleculeComponent> elements = g.getElements();
        double molarMass = 0;

        for (MoleculeComponent mc : elements) {
            molarMass += mc.getMultiplicity() * mc.getElement().getAtomicWeight();
        }

        return molarMass;
    }

    public static double massToMol(Molecule target, double massOfTarget) {
        double molarMass = getMolarMass(target);
        return massOfTarget / molarMass;
    }

    public static double volumeToMole(double volumeOfTarget) {
        return volumeOfTarget / 22.4;
    }

    public Stoichiometry(Equation eq) {
        this.eq = eq;
        moles = Double.MAX_VALUE;
        limiting = new ArrayList<Molecule>();
    }

    public void add(Molecule target, double quantity, char type) {
        int a = eq.getIndexOf(target);

        if (type == 'l') {
            quantity = volumeToMole(quantity);
        } else if (type == 'g') {
            quantity = massToMol(target, quantity);
        }

        double effectiveMoles = quantity / eq.getBalanceArray()[a];
        if (moles - effectiveMoles > .00000001) {
            moles = effectiveMoles;
            limiting = new ArrayList<Molecule>();
            limiting.add(target);
        } else if (Math.abs(moles - effectiveMoles) < .00000001) {
            limiting.add(target);
        }
        count++;
    }

    public void add(StoiData c) throws UnknownElementException {
        add(new Molecule(c.getMolecule()), c.getVal(), c.getType());
    }

    public double getResult(Molecule destination, char type) {
        int a = eq.getIndexOf(destination);

        double quantity = moles * eq.getBalanceArray()[a];

        if (type == 'l') {
            quantity = molToVolume(quantity);
        } else if (type == 'g') {
            quantity = molToMass(destination, quantity);
        }
        return round(quantity, 4);
    }

    public static double round(double value, int decimalPlace) {
        double power_of_ten = 1;
        // floating point arithmetic can be very tricky.
        // that's why I introduce a "fudge factor"
        double fudge_factor = 0.05;
        while (decimalPlace-- > 0) {
            power_of_ten *= 10.0d;
            fudge_factor /= 10.0d;
        }
        return Math.round((value + fudge_factor) * power_of_ten) / power_of_ten;
    }

    public String getLimiting() {
        boolean isFirst = true;
        if (count != limiting.size()) {
            String rep = "";
            for (Molecule c : limiting) {
                if (!isFirst) {
                    rep += " ," + c.toString();
                } else {
                    rep += c.toString();
                    isFirst = false;
                }
            }
            return rep;
        }

        return null;
    }

    public static double molToMass(Molecule target, double quantity) {
        double molarMass = getMolarMass(target);
        return quantity * molarMass;
    }

    public static double molToVolume(double quantity) {
        return quantity * 22.4;
    }


}
